<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebService TTS 持续测试 - 神算子</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
            padding: 30px;
            animation: fadeInUp 0.6s ease-out;
        }

        @keyframes fadeInUp {
            from {
                opacity: 0;
                transform: translateY(30px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .header {
            text-align: center;
            margin-bottom: 30px;
        }

        .header h1 {
            color: #333;
            font-size: 2.2em;
            margin-bottom: 10px;
            background: linear-gradient(135deg, #667eea, #764ba2);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .header p {
            color: #666;
            font-size: 1.1em;
        }

        .main-content {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 30px;
            margin-bottom: 30px;
        }

        @media (max-width: 768px) {
            .main-content {
                grid-template-columns: 1fr;
            }
        }

        .control-panel {
            background: #f8f9fa;
            border-radius: 15px;
            padding: 25px;
        }

        .control-panel h3 {
            color: #333;
            margin-bottom: 20px;
            font-size: 1.3em;
        }

        .form-group {
            margin-bottom: 20px;
        }

        .form-group label {
            display: block;
            margin-bottom: 8px;
            color: #333;
            font-weight: 600;
        }

        .form-group input, .form-group textarea, .form-group select {
            width: 100%;
            padding: 12px;
            border: 2px solid #e1e5e9;
            border-radius: 8px;
            font-size: 14px;
            font-family: inherit;
            transition: all 0.3s ease;
        }

        .form-group input:focus, .form-group textarea:focus, .form-group select:focus {
            outline: none;
            border-color: #667eea;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
        }

        .form-group textarea {
            resize: vertical;
            min-height: 100px;
        }

        .btn {
            padding: 12px 20px;
            border: none;
            border-radius: 8px;
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            margin-right: 10px;
            margin-bottom: 10px;
        }

        .btn-primary {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
        }

        .btn-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
        }

        .btn-success {
            background: #28a745;
            color: white;
        }

        .btn-danger {
            background: #dc3545;
            color: white;
        }

        .btn-secondary {
            background: #6c757d;
            color: white;
        }

        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none !important;
        }

        .log-panel {
            background: #f8f9fa;
            border-radius: 15px;
            padding: 25px;
        }

        .log-panel h3 {
            color: #333;
            margin-bottom: 20px;
            font-size: 1.3em;
        }

        .log-container {
            background: #2d3748;
            color: #e2e8f0;
            border-radius: 8px;
            padding: 15px;
            height: 400px;
            overflow-y: auto;
            font-family: 'Courier New', monospace;
            font-size: 13px;
            line-height: 1.4;
        }

        .log-entry {
            margin-bottom: 5px;
            padding: 3px 0;
        }

        .log-entry.success {
            color: #68d391;
        }

        .log-entry.error {
            color: #fc8181;
        }

        .log-entry.info {
            color: #63b3ed;
        }

        .log-entry.warning {
            color: #f6e05e;
        }

        .status-bar {
            background: #e9ecef;
            border-radius: 10px;
            padding: 15px;
            margin-bottom: 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 10px;
        }

        .status-item {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .status-indicator {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #dc3545;
        }

        .status-indicator.running {
            background: #28a745;
            animation: pulse 2s infinite;
        }

        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.5; }
            100% { opacity: 1; }
        }

        .audio-queue {
            background: #f8f9fa;
            border-radius: 15px;
            padding: 25px;
            margin-top: 20px;
        }

        .audio-queue h3 {
            color: #333;
            margin-bottom: 20px;
            font-size: 1.3em;
        }

        .audio-item {
            background: white;
            border-radius: 8px;
            padding: 15px;
            margin-bottom: 10px;
            border-left: 4px solid #667eea;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .audio-item .text {
            flex: 1;
            margin-right: 15px;
            font-size: 14px;
            color: #333;
        }

        .audio-item audio {
            max-width: 200px;
        }

        .predefined-texts {
            background: #f8f9fa;
            border-radius: 15px;
            padding: 25px;
            margin-top: 20px;
        }

        .predefined-texts h3 {
            color: #333;
            margin-bottom: 20px;
            font-size: 1.3em;
        }

        .text-item {
            background: white;
            border-radius: 8px;
            padding: 15px;
            margin-bottom: 10px;
            cursor: pointer;
            transition: all 0.3s ease;
            border: 2px solid transparent;
        }

        .text-item:hover {
            border-color: #667eea;
            transform: translateY(-2px);
        }

        .text-item .title {
            font-weight: 600;
            color: #333;
            margin-bottom: 5px;
        }

        .text-item .content {
            color: #666;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🚀 WebService TTS 持续测试</h1>
            <p>模拟持续发送文本到 WebService 接口进行语音合成</p>
        </div>

        <div class="status-bar">
            <div class="status-item">
                <div class="status-indicator" id="statusIndicator"></div>
                <span id="statusText">未运行</span>
            </div>
            <div class="status-item">
                <strong>已发送:</strong> <span id="sentCount">0</span> 次
            </div>
            <div class="status-item">
                <strong>成功:</strong> <span id="successCount">0</span> 次
            </div>
            <div class="status-item">
                <strong>失败:</strong> <span id="errorCount">0</span> 次
            </div>
        </div>

        <div class="main-content">
            <div class="control-panel">
                <h3>🎛️ 控制面板</h3>
                
                <div class="form-group">
                    <label for="customText">自定义文本</label>
                    <textarea id="customText" placeholder="输入要转换的文本...">你好，这是WebService TTS测试。</textarea>
                </div>

                <div class="form-group">
                    <label for="interval">发送间隔 (秒)</label>
                    <input type="number" id="interval" value="3" min="1" max="60">
                </div>

                <div class="form-group">
                    <label for="maxRequests">最大请求次数 (0=无限)</label>
                    <input type="number" id="maxRequests" value="10" min="0" max="1000">
                </div>

                <div class="form-group">
                    <label for="mode">测试模式</label>
                    <select id="mode">
                        <option value="single">单一文本重复</option>
                        <option value="predefined">预定义文本循环</option>
                        <option value="random">随机文本生成</option>
                    </select>
                </div>

                <div style="margin-top: 20px;">
                    <button class="btn btn-success" onclick="startTest()" id="startBtn">▶️ 开始测试</button>
                    <button class="btn btn-danger" onclick="stopTest()" id="stopBtn" disabled>⏹️ 停止测试</button>
                    <button class="btn btn-primary" onclick="sendSingle()">🎯 发送单次</button>
                    <button class="btn btn-secondary" onclick="clearLogs()">🗑️ 清空日志</button>
                </div>
            </div>

            <div class="log-panel">
                <h3>📋 实时日志</h3>
                <div class="log-container" id="logContainer">
                    <div class="log-entry info">[系统] WebService TTS 测试工具已就绪</div>
                </div>
            </div>
        </div>

        <div class="audio-queue">
            <h3>🎵 音频队列</h3>
            <div id="audioQueue">
                <p style="color: #666; text-align: center;">暂无音频文件</p>
            </div>
        </div>

        <div class="predefined-texts">
            <h3>📝 预定义文本 (点击选择)</h3>
            <div class="text-item" onclick="selectText(this)">
                <div class="title">问候语</div>
                <div class="content">你好，欢迎使用神算子WebService TTS服务！</div>
            </div>
            <div class="text-item" onclick="selectText(this)">
                <div class="title">新闻播报</div>
                <div class="content">今天是2024年1月15日，天气晴朗，温度适宜。让我们来看看今天的重要新闻。</div>
            </div>
            <div class="text-item" onclick="selectText(this)">
                <div class="title">技术介绍</div>
                <div class="content">Edge TTS是微软开发的高质量文本转语音技术，支持多种语言和语音风格。</div>
            </div>
            <div class="text-item" onclick="selectText(this)">
                <div class="title">长文本测试</div>
                <div class="content">这是一个较长的文本测试，用于验证WebService接口在处理长文本时的性能表现。文本转语音技术在现代应用中扮演着重要角色，它能够将文字信息转换为自然流畅的语音输出。</div>
            </div>
            <div class="text-item" onclick="selectText(this)">
                <div class="title">数字测试</div>
                <div class="content">今天的温度是25摄氏度，湿度60%，风速每小时15公里。电话号码：138-0013-8888。</div>
            </div>
        </div>
    </div>

    <script>
        const API_BASE = window.location.origin;
        let isRunning = false;
        let intervalId = null;
        let sentCount = 0;
        let successCount = 0;
        let errorCount = 0;
        let audioCounter = 0;
        let audioQueue = [];
        let currentlyPlaying = false;

        const predefinedTexts = [
            "你好，欢迎使用神算子WebService TTS服务！",
            "今天是2024年1月15日，天气晴朗，温度适宜。",
            "Edge TTS是微软开发的高质量文本转语音技术。",
            "这是一个WebService接口测试，正在验证语音合成功能。",
            "人工智能技术正在改变我们的生活方式。",
            "语音合成技术让机器能够说话，这是科技进步的体现。",
            "测试文本：今天的股价上涨了3.5%，交易量达到1000万股。",
            "欢迎收听今天的天气预报，明天将有小雨，请注意携带雨具。"
        ];

        function log(message, type = 'info') {
            const logContainer = document.getElementById('logContainer');
            const timestamp = new Date().toLocaleTimeString();
            const logEntry = document.createElement('div');
            logEntry.className = `log-entry ${type}`;
            logEntry.textContent = `[${timestamp}] ${message}`;
            logContainer.appendChild(logEntry);
            logContainer.scrollTop = logContainer.scrollHeight;
        }

        function updateStatus() {
            document.getElementById('sentCount').textContent = sentCount;
            document.getElementById('successCount').textContent = successCount;
            document.getElementById('errorCount').textContent = errorCount;
        }

        function updateUI(running) {
            isRunning = running;
            const statusIndicator = document.getElementById('statusIndicator');
            const statusText = document.getElementById('statusText');
            const startBtn = document.getElementById('startBtn');
            const stopBtn = document.getElementById('stopBtn');

            if (running) {
                statusIndicator.classList.add('running');
                statusText.textContent = '运行中';
                startBtn.disabled = true;
                stopBtn.disabled = false;
            } else {
                statusIndicator.classList.remove('running');
                statusText.textContent = '已停止';
                startBtn.disabled = false;
                stopBtn.disabled = true;
            }
        }

        function getTextToSend() {
            const mode = document.getElementById('mode').value;
            const customText = document.getElementById('customText').value;

            switch (mode) {
                case 'single':
                    return customText || '默认测试文本';
                case 'predefined':
                    return predefinedTexts[sentCount % predefinedTexts.length];
                case 'random':
                    const randomIndex = Math.floor(Math.random() * predefinedTexts.length);
                    return predefinedTexts[randomIndex];
                default:
                    return customText;
            }
        }

        let websocket = null;
        let isConnected = false;

        function connectWebSocket() {
            return new Promise((resolve, reject) => {
                try {
                    const wsUrl = `ws://${window.location.host}/tts/ws`;
                    websocket = new WebSocket(wsUrl);
                    
                    websocket.onopen = function(event) {
                        isConnected = true;
                        log('🔗 WebSocket连接已建立', 'success');
                        resolve();
                    };
                    
                    websocket.onmessage = function(event) {
                        try {
                            const data = JSON.parse(event.data);
                            handleWebSocketMessage(data);
                        } catch (error) {
                            log(`❌ 解析消息失败: ${error.message}`, 'error');
                        }
                    };
                    
                    websocket.onclose = function(event) {
                        isConnected = false;
                        log('🔌 WebSocket连接已关闭', 'warning');
                        if (isRunning) {
                            log('🔄 尝试重新连接...', 'info');
                            setTimeout(() => {
                                connectWebSocket().catch(err => {
                                    log(`❌ 重连失败: ${err.message}`, 'error');
                                });
                            }, 2000);
                        }
                    };
                    
                    websocket.onerror = function(error) {
                        log(`❌ WebSocket错误: ${error}`, 'error');
                        reject(error);
                    };
                    
                } catch (error) {
                    reject(error);
                }
            });
        }

        function handleWebSocketMessage(data) {
            switch (data.type) {
                case 'status':
                    log(`ℹ️ ${data.message}`, 'info');
                    break;
                    
                case 'audio':
                    successCount++;
                    log(`✅ 收到音频，大小: ${(data.size / 1024).toFixed(2)} KB`, 'success');
                    log(`🔍 Base64数据长度: ${data.data.length}`, 'info');
                    
                    try {
                        // 将base64音频数据转换为Blob
                        log(`🔄 开始解码base64音频数据...`, 'info');
                        const binaryString = atob(data.data);
                        log(`🔄 Base64解码完成，二进制长度: ${binaryString.length}`, 'info');
                        
                        const bytes = new Uint8Array(binaryString.length);
                        for (let i = 0; i < binaryString.length; i++) {
                            bytes[i] = binaryString.charCodeAt(i);
                        }
                        log(`🔄 字节数组创建完成，长度: ${bytes.length}`, 'info');
                        
                        const audioBlob = new Blob([bytes], { type: 'audio/mpeg' });
                        log(`🔄 音频Blob创建完成，大小: ${audioBlob.size} bytes`, 'info');
                        
                        const audioUrl = URL.createObjectURL(audioBlob);
                        log(`🎵 音频URL创建成功: ${audioUrl.substring(0, 50)}...`, 'success');
                        
                        addAudioToQueue(data.text, audioUrl);
                        
                    } catch (error) {
                        log(`❌ 音频处理失败: ${error.message}`, 'error');
                        console.error('音频处理详细错误:', error);
                    }
                    break;
                    
                case 'error':
                    errorCount++;
                    log(`❌ 服务器错误: ${data.message}`, 'error');
                    break;
                    
                default:
                    log(`❓ 未知消息类型: ${data.type}`, 'warning');
            }
            
            updateStatus();
        }

        async function sendTTSRequest(text) {
            try {
                if (!isConnected) {
                    log('🔄 WebSocket未连接，尝试连接...', 'info');
                    await connectWebSocket();
                }
                
                log(`📤 发送请求: ${text.substring(0, 30)}${text.length > 30 ? '...' : ''}`, 'info');
                
                const message = {
                    text: text,
                    voice: 'zh-CN-XiaoxiaoNeural'
                };
                
                websocket.send(JSON.stringify(message));
                sentCount++;
                updateStatus();
                
            } catch (error) {
                errorCount++;
                log(`❌ 发送失败: ${error.message}`, 'error');
                sentCount++;
                updateStatus();
            }
        }

        function addAudioToQueue(text, audioUrl) {
            const audioQueueElement = document.getElementById('audioQueue');
            
            // 清空提示文本
            if (audioQueueElement.children.length === 1 && audioQueueElement.children[0].tagName === 'P') {
                audioQueueElement.innerHTML = '';
            }

            audioCounter++;
            const audioItem = {
                id: audioCounter,
                text: text,
                url: audioUrl,
                element: null,
                audio: null,
                played: false
            };
            
            // 创建音频显示元素
            const audioItemElement = document.createElement('div');
            audioItemElement.className = 'audio-item';
            audioItemElement.id = `audio-item-${audioCounter}`;
            
            // 创建音频元素
            const audio = document.createElement('audio');
            audio.controls = true;
            audio.preload = 'auto';
            audio.src = audioUrl;
            
            // 添加音频事件监听
            audio.addEventListener('loadeddata', function() {
                log(`🎵 音频 #${audioCounter} 加载完成`, 'success');
            });
            
            audio.addEventListener('error', function(e) {
                log(`❌ 音频 #${audioCounter} 播放错误: ${e.message || '未知错误'}`, 'error');
                currentlyPlaying = false;
                playNextAudio(); // 播放错误时继续播放下一个
            });
            
            audio.addEventListener('ended', function() {
                log(`✅ 音频 #${audioItem.id} 播放完成`, 'info');
                const statusElement = document.getElementById(`status-${audioItem.id}`);
                if (statusElement) {
                    statusElement.textContent = '播放完成';
                    statusElement.style.color = '#666';
                }
                currentlyPlaying = false;
                playNextAudio(); // 播放完成后播放下一个
            });
            
            audioItemElement.innerHTML = `
                <div class="text">
                    <strong>#${audioCounter}</strong> ${text.substring(0, 50)}${text.length > 50 ? '...' : ''}
                    <br><small id="status-${audioCounter}" style="color: #666;">等待播放...</small>
                </div>
            `;
            
            audioItemElement.appendChild(audio);
            audioItem.element = audioItemElement;
            audioItem.audio = audio;
            
            // 添加到队列
            audioQueue.push(audioItem);
            audioQueueElement.insertBefore(audioItemElement, audioQueueElement.firstChild);
            
            // 限制显示的音频数量
            if (audioQueueElement.children.length > 10) {
                const lastChild = audioQueueElement.lastChild;
                const lastAudio = lastChild.querySelector('audio');
                if (lastAudio && lastAudio.src) {
                    URL.revokeObjectURL(lastAudio.src); // 释放内存
                }
                audioQueueElement.removeChild(lastChild);
                // 同时从队列中移除
                audioQueue = audioQueue.slice(0, 10);
            }
            
            log(`🎵 音频 #${audioCounter} 已添加到播放队列 (队列长度: ${audioQueue.length})`, 'info');
            
            // 如果当前没有音频在播放，开始播放
            if (!currentlyPlaying) {
                playNextAudio();
            }
        }
        
        function playNextAudio() {
            if (currentlyPlaying || audioQueue.length === 0) {
                return;
            }
            
            // 找到第一个未播放的音频
            const nextAudio = audioQueue.find(item => !item.played);
            if (!nextAudio) {
                log(`🎵 所有音频播放完成`, 'info');
                return;
            }
            
            currentlyPlaying = true;
            nextAudio.played = true;
            
            // 更新状态显示
            const statusElement = document.getElementById(`status-${nextAudio.id}`);
            if (statusElement) {
                statusElement.textContent = '正在播放...';
                statusElement.style.color = '#28a745';
            }
            
            log(`🎵 开始播放音频 #${nextAudio.id}`, 'success');
            
            nextAudio.audio.play().then(() => {
                log(`🎵 音频 #${nextAudio.id} 播放开始`, 'success');
            }).catch(error => {
                log(`⚠️ 音频 #${nextAudio.id} 自动播放失败 (浏览器限制): ${error.message}`, 'warning');
                const statusElement = document.getElementById(`status-${nextAudio.id}`);
                if (statusElement) {
                    statusElement.textContent = '播放失败，请手动播放';
                    statusElement.style.color = '#dc3545';
                }
                currentlyPlaying = false;
                playNextAudio(); // 播放失败时继续下一个
            });
        }

        async function startTest() {
            if (isRunning) return;
            
            const interval = parseInt(document.getElementById('interval').value) * 1000;
            const maxRequests = parseInt(document.getElementById('maxRequests').value);
            
            log(`🚀 开始持续测试，间隔: ${interval/1000}秒，最大次数: ${maxRequests || '无限'}`, 'info');
            
            try {
                // 建立WebSocket连接
                if (!isConnected) {
                    log('🔄 建立WebSocket连接...', 'info');
                    await connectWebSocket();
                }
                
                updateUI(true);
                
                let requestCount = 0;
                
                const sendRequest = async () => {
                    if (!isRunning) return;
                    
                    if (maxRequests > 0 && requestCount >= maxRequests) {
                        log(`🏁 达到最大请求次数 (${maxRequests})，自动停止`, 'warning');
                        stopTest();
                        return;
                    }
                    
                    const text = getTextToSend();
                    await sendTTSRequest(text);
                    requestCount++;
                };
                
                // 立即发送第一个请求
                await sendRequest();
                
                // 设置定时器
                intervalId = setInterval(sendRequest, interval);
                
            } catch (error) {
                log(`❌ 启动测试失败: ${error.message}`, 'error');
                updateUI(false);
            }
        }

        function stopTest() {
            if (!isRunning) return;
            
            log('⏹️ 停止持续测试', 'warning');
            
            if (intervalId) {
                clearInterval(intervalId);
                intervalId = null;
            }
            
            // 关闭WebSocket连接
            if (websocket && isConnected) {
                websocket.close();
                log('🔌 WebSocket连接已关闭', 'info');
            }
            
            updateUI(false);
        }

        async function sendSingle() {
            const text = document.getElementById('customText').value || '单次测试文本';
            log('🎯 发送单次请求', 'info');
            await sendTTSRequest(text);
        }

        function clearLogs() {
            const logContainer = document.getElementById('logContainer');
            logContainer.innerHTML = '<div class="log-entry info">[系统] 日志已清空</div>';
            
            // 重置计数器
            sentCount = 0;
            successCount = 0;
            errorCount = 0;
            audioCounter = 0;
            updateStatus();
            
            // 清空音频队列
            audioQueue = [];
            currentlyPlaying = false;
            const audioQueueElement = document.getElementById('audioQueue');
            audioQueueElement.innerHTML = '<p style="color: #666; text-align: center;">暂无音频文件</p>';
        }

        function selectText(element) {
            const content = element.querySelector('.content').textContent;
            document.getElementById('customText').value = content;
            
            // 高亮选中的文本项
            document.querySelectorAll('.text-item').forEach(item => {
                item.style.borderColor = 'transparent';
            });
            element.style.borderColor = '#667eea';
            
            log(`📝 已选择预定义文本: ${content.substring(0, 30)}...`, 'info');
        }

        // 页面加载完成后的初始化
        document.addEventListener('DOMContentLoaded', function() {
            log('[系统] WebService TTS 测试工具初始化完成', 'success');
            updateStatus();
        });

        // 页面卸载时清理
        window.addEventListener('beforeunload', function() {
            if (isRunning) {
                stopTest();
            }
        });
    </script>
</body>
</html>